#include "TimeUtil.h"
#include <deque>
#include <algorithm>

#ifndef _linux_
#include <process.h>
#endif

#ifndef _linux_
#include "util.h"
#endif

#include "memrec.h"

#define new DBG_NEW

tTimer::tTimer()
{
    TimerCount = 20030;
    bStopSignal = false;
#ifndef _linux_
    hAccessMutex = CreateMutex(NULL, FALSE, NULL);
	hThread = NULL;
#endif
}

tTimer::~tTimer()
{
#ifndef _linux_
	if (hThread != 0)
	{
		TerminateThread(hThread, 0);
		CloseHandle(hThread);
	}
	CloseHandle(hAccessMutex);
#else
	//    pthread_cancel(thread_id);
#endif
}

#ifndef _linux_
unsigned int tTimer::Thread(void *param)
#else
	void* tTimer::Thread(void *param)
#endif
{
    tTimer* pThis = (tTimer*)param;
#ifndef _linux_
    while (1)
    {
		Sleep(100);
		AUTO_MUTEX lock(pThis->hAccessMutex);
		if (pThis->bStopSignal)
		{
			break;
		}
		// Check which timer is dued.
		DWORD CurTick = GetTickCount();
		std::deque<tTimerItem>::iterator it;
		for (it = pThis->Items.begin(); it != pThis->Items.end();)
		{
			if (it->duetime <= CurTick)
			{
				it->pNotify->NotifyMessage(NT_SIGNAL, it->TimerID, it->Data, NULL, 0);
				if (it->bRepeat)
				{
					CurTick = GetTickCount();
					it->duetime = CurTick + it->interval;
					it++;
				}
				else
				{
					it = pThis->Items.erase(it);
					if (it == pThis->Items.end())
						break;
				}
			}
			else 
				it++;
		}
    }
	return 0;
#else
    while(1)
    {
		timespec ts;
		//timespec tsleft;
		ts.tv_sec = 0;
		ts.tv_nsec = 1000*1000*100;
		if (pThis->bStopSignal)
		{
			break;
		}
		int rval = nanosleep(&ts, &ts);
	
		timeval tv;
		gettimeofday(&tv, NULL);
		UINT64 CurTime = tv.tv_sec * 1000 + tv.tv_usec/1000;
		pThis->mutex.Wait();
		std::deque<tTimerItem>::iterator it;
		for (it = pThis->Items.begin(); it != pThis->Items.end();)
		{
			if (it->duetime <= CurTime)
			{
				it->pNotify->NotifyMessage(NT_SIGNAL, it->TimerID, it->Data, NULL, 0);
				if (it->bRepeat)
				{
					it->duetime = CurTime + it->interval;
					it++;
				}
				else
				{
					it = pThis->Items.erase(it);
					if (it == pThis->Items.end())
						break;
				}
			}
			else 
				it++;
		}
		pThis->mutex.Release();
    }
#endif
}

unsigned int tTimer::SetTimer(DWORD ms, IMsgNotify * notify, bool bRepeat, 
							  long Data)
{
    tTimerItem ti;
#ifndef _linux_
    ti.duetime = GetTickCount() + ms;
#else
    timeval tv;
    gettimeofday(&tv, NULL);
    UINT64 CurTime = tv.tv_sec * 1000 + tv.tv_usec/1000;
    ti.duetime = CurTime + ms;
#endif
    ti.pNotify = notify;
    ti.bRepeat = bRepeat;
    ti.interval = ms;
    ti.TimerID = TimerCount++;
    ti.Data = Data;

#ifndef _linux_
    AUTO_MUTEX lock(hAccessMutex);
#else
    mutex.Wait();
#endif

    Items.push_back(ti);

#ifdef _linux_
    mutex.Release();
#endif
    return ti.TimerID;
}

void tTimer::CancelTimer(unsigned int TimerID)
{
#ifndef _linux_
    AUTO_MUTEX lock(hAccessMutex);
#else
    mutex.Wait();
#endif
	while (true)
	{
		std::deque<tTimerItem>::iterator it_res;
		tTimerItem ti;
		ti.TimerID = TimerID;
		it_res = find(Items.begin(), Items.end(), ti);
		if (it_res == Items.end())
			break;
		Items.erase(it_res);
		break;
	}

#ifdef _linux_
    mutex.Release();
#endif
}

void tTimer::ReStartTimer(unsigned int TimerID)
{
#ifndef _linux_
    AUTO_MUTEX lock(hAccessMutex);
#else
    mutex.Wait();
#endif
    while (true)
    {
		std::deque<tTimerItem>::iterator it_res;
		tTimerItem ti;
		ti.TimerID = TimerID;
		it_res = find(Items.begin(), Items.end(), ti);
		if (it_res == Items.end())
			break;
#ifndef _linux_
		it_res->duetime = GetTickCount() + it_res->interval;
#else
		timeval tv;
		gettimeofday(&tv, NULL);
		UINT64 CurTime = tv.tv_sec * 1000 + tv.tv_usec/1000;
		ti.duetime = CurTime + it_res->interval;
#endif
		break;
    }

#ifdef _linux_
    mutex.Release();
#endif
}

void tTimer::Start()
{
    bStopSignal = false;
#ifndef _linux_
    // start up the thread.
    hThread = (HANDLE)_beginthreadex(NULL, 0, Thread, this, 0, 0);
#else
    thread_id = 0;
    int r = pthread_create(&thread_id, NULL, Thread, this);
    if (r != 0)
    {
		printf("Internal error:Can not create thread."	\
			   "error: %d\n", errno);
    }
#endif


}

void tTimer::Stop()
{

    bStopSignal = true;
#ifndef _linux_
	if (hThread != 0)
	{
		WaitForSingleObject(hThread, INFINITE);
		CloseHandle(hThread);
		hThread = 0;
	}
#else
	if (thread_id != 0)
	{
		pthread_join(thread_id, NULL);
    	thread_id = 0;
	}
#endif
    bStopSignal = false;
    Items.clear();
}
